package com.wisecoach.gatewayplus.bind;

import com.wisecoach.gatewayplus.annotation.TransientMap;
import com.wisecoach.gatewayplus.annotation.TransientValue;
import com.wisecoach.util.CollectionUtils;
import com.wisecoach.util.JacksonUtils;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 注解的参数解析器
 * {@code @author:} wisecoach
 * {@code @date:} 2023/3/6 下午12:20
 * {@code @version:} 1.0.0
 */
public class AnnotatedContractResolver implements ContractResolver {

    private static final String NORMAL_ARGS = "normal_args";
    private static final String TRANSIENT_PAIRS = "transient_pairs";
    private static final String TRANSIENT_MAP = "transient_map";


    @SuppressWarnings("unchecked")
    @Override
    public ContractArgs resolveArgs(Object[] args, Method method) {
        Map<String, List<?>> splitedArgs = splitArgs(args, method);
        List<Object> normalArgs = (List<Object>) splitedArgs.get(NORMAL_ARGS);
        List<TransientPair> transientPairs = (List<TransientPair>) splitedArgs.get(TRANSIENT_PAIRS);
        List<Map<String, Object>> transientMaps = (List<Map<String, Object>>) splitedArgs.get(TRANSIENT_MAP);
        String[] resolvedNormalArgs = resolveNormalArgs(normalArgs);
        Map<String, byte[]> resolvedTransientMap = null;
        if (!CollectionUtils.isEmpty(transientPairs)) {
            resolvedTransientMap = resolveTransientMap(transientPairs);
        }
        if (!CollectionUtils.isEmpty(transientMaps)) {
            resolvedTransientMap = resolveTransientMap(transientMaps.get(0));
        }
        return new ContractArgs(resolvedNormalArgs, resolvedTransientMap);
    }

    @Override
    public Object resolveResult(byte[] result, Method method) {
        if (method.getReturnType().isAssignableFrom(void.class)) {
            return null;
        } else if (method.getReturnType().isAssignableFrom(byte[].class)) {
            return result;
        } else if (method.getReturnType().isAssignableFrom(String.class)) {
            return new String(result);
        }
        return JacksonUtils.deserialize(result, method.getReturnType());
    }

    protected Map<String, List<?>> splitArgs(Object[] args, Method method) {
        HashMap<String, List<?>> map = new HashMap<>();
        ArrayList<Object> normalArgs = new ArrayList<>();
        ArrayList<Object> transientPairs = new ArrayList<>();
        ArrayList<Object> transientMaps = new ArrayList<>();
        map.put(NORMAL_ARGS, normalArgs);
        map.put(TRANSIENT_PAIRS, transientPairs);
        map.put(TRANSIENT_MAP, transientMaps);

        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            TransientValue transientValue = parameters[i].getAnnotation(TransientValue.class);
            TransientMap transientMap = parameters[i].getAnnotation(com.wisecoach.gatewayplus.annotation.TransientMap.class);
            if (transientValue != null) {
                TransientPair pair = new TransientPair(transientValue.value(), args[i]);
                transientPairs.add(pair);
            } else if (transientMap != null) {
                if (args[i] instanceof Map) {
                    throw new BindException("TransientMap注解的参数必须为Map类型");
                }
                transientMaps.add(args[i]);
            } else {
                normalArgs.add(args[i]);
            }
        }
        if (transientMaps.size() > 1) {
            throw new BindException("transientMap数量不可超过一个");
        }
        if (transientMaps.size() > 0 && transientPairs.size() > 0) {
            throw new BindException("transientMap和transientValue不可共用，请根据情况选择一个使用");
        }
        return map;
    }

    protected String[] resolveNormalArgs(List<Object> args) {
        if (args == null) {
            return new String[]{};
        }
        return args.stream().map(this::serialize).collect(Collectors.toList()).toArray(new String[]{});
    }

    protected Map<String, byte[]> resolveTransientMap(Map<String, Object> rawTransientMap) {
        HashMap<String, byte[]> map = new HashMap<>();
        if (!CollectionUtils.isEmpty(rawTransientMap)) {
            rawTransientMap.forEach((key, value) -> {
                map.put(key, serializeAsBytes(value));
            });
        }
        return map;
    }

    protected Map<String, byte[]> resolveTransientMap(List<TransientPair> pairs) {
        HashMap<String, byte[]> map = new HashMap<>();
        if (!CollectionUtils.isEmpty(pairs)) {
            pairs.forEach((pair) -> {
                map.put(pair.getKey(), serializeAsBytes(pair.getValue()));
            });
        }
        return map;
    }

    /**
     * 序列化为String，这里注意对于已经是String的，则不要走jackson，避免多一个""
     */
    private String serialize(Object obj) {
        if (obj instanceof String) {
            return (String) obj;
        }
        return JacksonUtils.serialize(obj);
    }

    /**
     * 序列化为byte[]，这里注意对于已经是String的，则不要走jackson，避免多一个""
     */
    private byte[] serializeAsBytes(Object obj) {
        if (obj instanceof String) {
            return ((String) obj).getBytes();
        }
        return JacksonUtils.serializeAsBytes(obj);
    }
}
